package com.haohope.excel;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSON;
import com.haohope.excel.annotations.ExcelEntity;
import com.haohope.excel.annotations.ExcelProperty;
import com.haohope.excel.enums.ExcelHeadCol;
import com.haohope.excel.exceptions.ExcelException;
import com.haohope.excel.rules.IExcelRule;
import com.haohope.excel.trans.IExcelTrans;
import com.haohope.excel.types.NoneType;
import com.haohope.excel.utils.DateUtil;
import com.haohope.excel.utils.NumberUtil;

/**
 * 名称: IRowReader.java<br>
 * 描述: <br>
 * 类型: JAVA<br>
 * 最近修改时间:2016年7月5日 上午10:28:06<br>
 * 
 * @since 2016年7月5日
 * @author “”
 */
public abstract class IExcelRowReader {
	private static Logger LOG = Logger.getLogger(IExcelRowReader.class);
	@SuppressWarnings("rawtypes")
	protected Class tmpClass = null;
	private Integer tmpSheet = null;
	private Integer tmpHeadRow = null;
	private Integer tmpHeadRowsheet = null;
	public IExcelRowReader() {}
	@SuppressWarnings("rawtypes")
	public IExcelRowReader(Class tmpClass) {
		this.tmpClass = tmpClass;
	}
	public IExcelRowReader(Integer tmpSheet,Integer tmpHeadRow,Integer tmpHeadRowsheet) {
		this.tmpSheet = tmpSheet;
		this.tmpHeadRow = tmpHeadRow;
		this.tmpHeadRowsheet = tmpHeadRowsheet;
	}
	@SuppressWarnings("rawtypes")
	public IExcelRowReader(Class tmpClass,Integer tmpSheet,Integer tmpHeadRow,Integer tmpHeadRowsheet) {
		this.tmpClass = tmpClass;
		this.tmpSheet = tmpSheet;
		this.tmpHeadRow = tmpHeadRow;
		this.tmpHeadRowsheet = tmpHeadRowsheet;
	}
	private boolean isTmp() {
		return (null!=tmpClass && null!=tmpSheet && null!=tmpHeadRow && null!=tmpHeadRowsheet);
	}
	/**
	 * excel处理结果
	 */
	private ExcelResult result = new ExcelResult();
    /**
	   * 正则表达式缓存
	 */
	private static final Map<String,Pattern> PATTERNS_CACHE = new ConcurrentHashMap<String,Pattern>();
	/**
	   *设置合并单元格列
	 */
	protected abstract void appendRef(String ref);
	protected abstract Object getObject(final String sheetname,final int sheetIndex, final int curRow, Map<String,String> rowObj)throws ExcelException;
	protected boolean ispass(Map<String,String> rowObj) {
		if(null==rowObj) {
			return true;
		}
		String a = rowObj.containsKey("a")?rowObj.get("a"):rowObj.get("A");
		String b = rowObj.containsKey("b")?rowObj.get("b"):rowObj.get("B");
		String c = rowObj.containsKey("c")?rowObj.get("c"):rowObj.get("C");
		if(StringUtils.isEmpty(a) && StringUtils.isEmpty(b) && StringUtils.isEmpty(c)) {
			return true;
		}else {
			return false;
		}
	}
	protected final void appendRow(final String sheetname,final int sheetIndex, final int curRow, Map<String,String> rowObj){
		try{
			LOG.info("[sheetname:"+sheetname+",sheet:"+sheetIndex+",row:"+curRow+"]"+rowObj);
			Object obj = ispass(rowObj)?null:getObject(sheetname,sheetIndex,curRow,rowObj);
			result.getSucceses().add(rowObj);
			if(null!=obj) result.getDatas().add(obj);
		}catch(Exception e){
			LOG.error("excel解析数据失败:"+rowObj, e);
			if(e instanceof ExcelException){
				rowObj.put("sheet", sheetIndex+"");
				rowObj.put("row", curRow+"");
				rowObj.put("error", e.getMessage());
			}
			Map<String,String> tmpObj = new HashMap<String,String>();
			for (Map.Entry<String, String> m : rowObj.entrySet()) { 
				tmpObj.put(m.getKey(), m.getValue());   
			}
			result.getErrors().add(tmpObj);
		}
	}
	/**
	 * 
	 * 加入其它的数据
	 */
	private void addother(String sheetname,int sheet,int row,Map<String,String> rowObj) {
		List<Map<String,String>> others = result.getOthers();
		rowObj.put("sheetname", sheetname);
		rowObj.put("sheet", sheet+"");
		rowObj.put("row", row+"");
		others.add(rowObj);
		result.setOthers(others);
	}
	private void addupheads(String sheetname,int sheet,int row,Map<String,String> rowObj) {
		List<Map<String,String>> others = result.getUpheads();
		rowObj.put("sheetname", sheetname);
		rowObj.put("sheet", sheet+"");
		rowObj.put("row", row+"");
		others.add(rowObj);
		result.setUpheads(others);
	}
	/**
	 * 
	 * 获取多个指定行列的数据
	 */
	private void addrowobjs(ExcelProperty pro,String sheetname,int sheet,int row,Map<String,String> rowObj) {
		int row_ = pro.row();
		//列名
		String col = pro.col().getCharacter().toLowerCase();
		//获取其它数据
		if(row_ == row) {
			String value = rowObj.get(col);
			if(StringUtils.isEmpty(value)) {
				value = rowObj.get(col.toLowerCase());
			}
			if(StringUtils.isEmpty(value)) {
				value = rowObj.get(col.toUpperCase());
			}
			if(StringUtils.isNotEmpty(value)) {
				//value = rowObj.get(col.toUpperCase());
				if(NumberUtil.isNumeric(value.replace(",", ""))) {
					value = value.replace(",", "");
				}
			}
			if(1==sheet) {
				(result.getRowObj()).put(col+row_, value);
			}
			Map<String, String> rowObj_ = result.getRowObjs().get(sheetname);
			if(null==rowObj_) {
				rowObj_ = new HashMap<String, String>();
			}
			rowObj_.put(col+row_, rowObj.get(col));
			result.getRowObjs().put(sheetname, rowObj_);
		}
	}
	//protected Object map2bean() {}
	@SuppressWarnings({ "unchecked", "rawtypes" })
	protected Object map2bean(String sheetname,int sheet,int row,Map<String,String> rowObj,Class clazz) throws ExcelException{
		result.getSheetindex_name().put(sheet, sheetname);
		result.getSheetname_index().put(sheetname, sheet);
		if(null==clazz) {
			throw new IllegalArgumentException(" class is null.");
		}
		ExcelEntity excel = (ExcelEntity) clazz.getAnnotation(ExcelEntity.class);
		if(null==excel) {
			return JSON.parseObject(JSON.toJSONString(rowObj), clazz);
		}
		//读取的表格
    	int sheet_ = isTmp()?tmpSheet:excel.sheet();
    	if(sheet_ > 0 && sheet_ < sheet) {
    		//其它sheet数据
    		addother(sheetname,sheet,row,rowObj);
    		return null;
    	}
    	//表头所在行
    	int headRow = isTmp()?tmpHeadRow:excel.headRow();
    	//设置表头（只能在第一页）
    	int headRowsheet = isTmp()?tmpHeadRowsheet:excel.headRowsheet();//表头作用页
    	if(headRow == row) {
    		Map<String,String> head = new HashMap<String,String>();
    		for(Map.Entry<String, String> m:rowObj.entrySet())head.put(m.getKey(),m.getValue());
    		if(1==sheet) {
    			result.setHead(head);
    			result.getHeads().put(sheetname, head);
    		}else if(headRowsheet >= 1) {
    			result.getHeads().put(sheetname, head);
    		}
    	}
    	
    	//excel表头以上的数据不获取
    	if(row <= headRow && (headRowsheet >= 1 || (headRowsheet < 1 && 1==sheet))) {
    		addupheads(sheetname,sheet,row,rowObj);
    	}
    	//class 实例化
    	Object t = null;
    	try {
			t = clazz.newInstance();
		} catch (Exception e) {
			e.printStackTrace();
			throw new ExcelException("类["+clazz.getName()+"]无法实例化。");
		}
		for(Field field:clazz.getDeclaredFields()){
			field.setAccessible(true);
			ExcelProperty pro = field.getAnnotation(ExcelProperty.class);
			if(null==pro) continue;
			//获取多个指定行列的数据
			addrowobjs(pro,sheetname,sheet,row,rowObj);
			//行数
			//int row_ = pro.row();
			//列名
			//String col = pro.col().getCharacter();
			//获取其它数据
			//if(row_ == row)(result.getRowObj()).put(col+row_, rowObj.get(col));
			//只获取表头下面的数据
			if(row <= headRow && (headRowsheet >= 1 || (headRowsheet < 1 && 1==sheet)))continue;
			//if(!ExcelDataType.ARRAY.equals(datatype)) continue;
			//sheet name
			Boolean sheetnameflag = pro.sheetnameflag();
			Boolean sheetindexflag = pro.sheetindexflag();
			Boolean rowindexflag = pro.rowindexflag();
			//只能在第一页
			if(sheetnameflag || sheetindexflag || rowindexflag) {
				try {
					String type = field.getGenericType().toString();
					if(sheetnameflag) {
						if(type.equals("class java.lang.String")) {
							field.set(t, sheetname);
						}
					}else if(sheetindexflag) {
						if (type.equals("class java.lang.Integer")||type.equals("int")) {
	                    	field.set(t, sheet);
	                    }
					}else if(rowindexflag) {
						if (type.equals("class java.lang.Integer")||type.equals("int")) {
	                    	field.set(t, row);
	                    }
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
				continue;
			}
			if(pro.headCol() == ExcelHeadCol.NULL) {
				continue;
			}
			//数据表头
			String headCol = pro.headCol().getCharacter();
			//是否必填项
			Boolean required = pro.required();
			//通过数据表头获取的值
			String value = rowObj.get(headCol);
			if(StringUtils.isEmpty(value)) {
				value = rowObj.get(headCol.toLowerCase());
			}
			if(StringUtils.isEmpty(value)) {
				value = rowObj.get(headCol.toUpperCase());
			}
			if(StringUtils.isNotEmpty(value)) {
				//value = rowObj.get(col.toUpperCase());
				if(NumberUtil.isFloatingNumber(value.replace(",", ""))) {
					value = value.replace(",", "");
				}
			}
			//如果必填项 没有填写抛出异常
			if(required && StringUtils.isEmpty(value)) throw new ExcelException(pro.requiredMsg());
			//如果正则表达式不通过抛出异常
			String regexp = pro.regexp();
			if(!StringUtils.isEmpty(value) && !StringUtils.isEmpty(regexp) ){
				Pattern pattern = PATTERNS_CACHE.get(regexp);
				if(null==pattern){
					pattern = Pattern.compile(regexp);
					PATTERNS_CACHE.put(regexp, pattern);
				}
				if(!pattern.matcher(value).matches())
				throw new ExcelException(pro.regexpMsg());
			}
			//规则
            String errorMsg = IExcelRule.createRule(pro.rule()).errorMsg(sheetname, sheet, row, rowObj, value, clazz, field);
        	if(!StringUtils.isEmpty(errorMsg)) {
        		throw new ExcelException(errorMsg);
        	}
            try{
            	if(StringUtils.isEmpty(value)) {
            		String defalutValue = pro.defaultValue();
            		if(StringUtils.isEmpty(defalutValue)) continue;
            		value = defalutValue;
            	}
            	//值转换
            	value = IExcelTrans.createTrans(pro.trans()).transValue(sheetname, sheet, row, rowObj, value, clazz, field);
            	//复杂类型
            	Class classType = pro.type();
            	//数字验证
//            	if(IExcelRowReader.pattern.matcher(value).matches() && value.indexOf(".") > 0)
//    				value = value.substring(0,value.indexOf("."));
    			String type = field.getGenericType().toString();
    			//正整数
                if (type.equals("class java.lang.Integer")||type.equals("int")) {
                	value = str2numberstr(value);
                	field.set(t, Integer.parseInt(value));
                }
                else if (type.equals("class java.lang.Long")||type.equals("long")) {
                	value = str2numberstr(value);
                    field.set(t, Long.parseLong(value));
                }
                else if (type.equals("class java.lang.Float")||type.equals("float")) {
                	value = str2numberstr(value);
                    field.set(t, new BigDecimal(value).floatValue());
                }
                //浮点数
                else if (type.equals("class java.lang.Double")||type.equals("double")) {
                	value = str2numberstr(value);
                    field.set(t, new BigDecimal(value).doubleValue());
                }else if(type.equals("class java.math.BigDecimal")||type.equals("bigDecimal")) {
                	value = str2numberstr(value);
                	field.set(t, new BigDecimal(value));
                }
                //布尔值
                else  if(type.equals("class java.lang.Boolean")||type.equals("boolean")) {
                    field.set(t, Boolean.parseBoolean(value));
                }
                //日期
                else  if(type.equals("class java.util.Date")) {
                	String dataformat = pro.dateformat();
                	if(StringUtils.isEmpty(dataformat))dataformat = "yyyy-MM-dd HH:mm:ss";
                	DateFormat sdf = DateUtil.getDateFormat(dataformat);
                    field.set(t, sdf.parse(value));
                } else if(!NoneType.class.getName().equals(classType.getName())) {
                	field.set(t, JSON.parseObject(value, classType));
            	} else {
                    field.set(t, JSON.parseObject(value, field.getClass()));
                }
            } catch (IllegalArgumentException | IllegalAccessException | ParseException e) {
				e.printStackTrace();
				throw new ExcelException("原表格数据 位置：[sheet表格:"+sheetname+",单元格 :"+headCol+row+"] 值["+value+"]数据格式有误。");
			}
		}
    	return t;
	}
	private String str2numberstr(String str) {
		if (StringUtils.isEmpty(str)) {
			return "0";
		}
		str = str.trim().replace(" ", "");
		if (!str.startsWith(",") && !str.endsWith(",")) {
			str = str.replace(",", "");
		}
		if (!str.startsWith("，") && !str.endsWith("，")) {
			str = str.replace("，", "");
		}
		return str;
	}
	public ExcelResult getResult(){
		return result;
	}
}
